/*
*    Phantom Test Driver v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
 * PTDHTMLPane.java
 *
 * Created on January 1, 2006, 8:10 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

package ptd;

import javax.swing.JEditorPane;
import javax.swing.JTextPane;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.*;
import javax.swing.JScrollBar;
import java.awt.Color;
import java.awt.Insets;

import java.awt.*;              //for layout managers and more
import javax.swing.text.*;
import java.awt.event.*;
import java.awt.Shape;

import java.util.Date;
import java.util.Vector;
import ptd_suite_objects.SuiteBase;

import ssi_socket.*;

import java.awt.Font;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.StringReader;
import java.io.*;
import javax.swing.JButton;
import javax.swing.RepaintManager;

/**
 *
 * @author John
 */
//public class PTDHTMLPane extends JEditorPane implements PTDScriptComm, PTDDisplay{
public class PTDHTMLPane extends JTextPane implements PTDScriptComm, PTDDisplay, Printable{
    public class PrintBounder{
        public Vector<Point> Bounds;
        public boolean isInitialized;
        public int numPages;
        public PrintBounder(){
            Bounds = new Vector<Point>();
            isInitialized = false;
            numPages = 0;
        }
        
        public void calcPrintBounds(PageFormat pf, PTDSuiteDisplay aDisplay){
         
            Bounds.clear();
            numPages = 0;
            Container C = aDisplay.getParent();  //print begins at aDisplay now
            //if(C == null){
            //    return;
            //}
            
            double topMargin = 0;  // 1/4 inch
            double botMargin = 0;  // 1/4 inch
            double pageHeight = pf.getImageableHeight() - (topMargin+botMargin);
            double scale = 1.0;      // Descale the text so more fits on a page
            
            int dispH = C.getHeight();
            //int totalNumPages = (int)Math.ceil(scale * dispH / pageHeight);

            boolean bContinue = true;
            double start = 0;
            double end = pageHeight;
            int backoff = 0;
            while(bContinue){
                int a = C.getWidth()/2;
                int b = (int)end;
                int c = (int)start;
                if(C.contains(a, b)){
                    backoff = aDisplay.computePrintBackoff(c, b);
                }else{
                    backoff = 0;
                }
                end = end - backoff;
                Point P = new Point((int)start, (int)end);
                Main.debug_out("Adding: " + Integer.toString(P.x) + "," + Integer.toString(P.y));
                Bounds.add(P);
                numPages = numPages + 1;
                
                start = end+1;   // +1 for no overlap
                end = start + pageHeight-1;
                if(start >= dispH){
                    bContinue = false;
                }
            }
            
            isInitialized = true;
        }
    }

    
    
    //public boolean bCanGetInput;
    //public boolean bInputAvailable;
    public int EndOfOutput; // I dont believe this is used anymore
    public int StartOfOutput; // I dont believe this is used anymore
    public int CmdLock;  // I dont believe this is used anymore
    
    public String m_InputString; // I dont believe this is used anymore
    public Element OutputElement;
    public Element DateElement;
    public JScrollBar VertBar;
    
    
    public SimpleAttributeSet SuiteAttr;
    public SimpleAttributeSet TestcaseAttr;
    public SimpleAttributeSet ErrorAttr;
    public SimpleAttributeSet WarningAttr;
    public SimpleAttributeSet FailureAttr;
    public SimpleAttributeSet StandardAttr;
    
    public Vector<PTDSuiteDisplay> Displays;
    public PTDSuiteDisplay CurrDisplay;    
    public PTDSuiteDisplay OutputDisplay;
    public int iLevel;
    public int printBackoff;
    public int lastPrintedPage;
    public PrintBounder Bounder;
    public PTDRemoteMachine remoteMachine = null;
    //public int numPages;
    
    // Get rid of these
    
    
    // MYKEYLISTENER ////////////////////////////////
    
    /** Creates a new instance of PTDHTMLPane */
    public PTDHTMLPane() {
        //HTMLEditorKit aKit = new HTMLEditorKit();
        //this.setEditorKit(aKit);
            
        // Maybe change this for inputs
        this.setEditable(false);
        Bounder = new PrintBounder();
        
        CurrDisplay = null;
        OutputDisplay = null;
        iLevel = 0;
        printBackoff = 0;
        lastPrintedPage = -1;
        Displays = new Vector<PTDSuiteDisplay>();

        SuiteAttr = new SimpleAttributeSet();
        TestcaseAttr = new SimpleAttributeSet();
        ErrorAttr = new SimpleAttributeSet();
        WarningAttr = new SimpleAttributeSet();
        FailureAttr = new SimpleAttributeSet();
        StandardAttr = new SimpleAttributeSet();
        
        String sFont = "Courier New";
        
        StyleConstants.setFontFamily(SuiteAttr, sFont);
        StyleConstants.setFontSize(SuiteAttr, 20);
        StyleConstants.setBold(SuiteAttr, true);
        
        StyleConstants.setFontFamily(TestcaseAttr, sFont);
        StyleConstants.setFontSize(TestcaseAttr, 18);
        StyleConstants.setBold(TestcaseAttr, true);

        StyleConstants.setFontFamily(ErrorAttr, sFont);
        StyleConstants.setFontSize(ErrorAttr, 12);
        StyleConstants.setBold(ErrorAttr, true);
        StyleConstants.setForeground(ErrorAttr, PTDSuiteDisplay.ErrColor);

        StyleConstants.setFontFamily(WarningAttr, sFont);
        StyleConstants.setFontSize(WarningAttr, 12);
        StyleConstants.setBold(WarningAttr, true);
        StyleConstants.setForeground(WarningAttr, PTDSuiteDisplay.WarnColor);
        
        StyleConstants.setFontFamily(FailureAttr, sFont);
        StyleConstants.setFontSize(FailureAttr, 12);
        StyleConstants.setBold(FailureAttr, true);

        StyleConstants.setFontFamily(StandardAttr, sFont);
        StyleConstants.setFontSize(StandardAttr, 12);

                
        CmdLock = 0;
        m_InputString = "";
          
        addKeyListener(new java.awt.event.KeyAdapter(){
            public void keyTyped(java.awt.event.KeyEvent evt) {
                m_KeyTyped(evt);
            }
            public void keyPressed(java.awt.event.KeyEvent evt) {
                         
                if(evt.isControlDown()){
                   // Dont want to process event if control is pressed, yo...
                   return;
                }
                if(evt.isAltDown()){
                    // Dont want to process event if alt is pressed, yo...
                    return;
                }
                
                if(evt.getKeyCode() == evt.VK_LEFT){
                    return;
                }
                
                if(evt.getKeyCode() == evt.VK_RIGHT){
                    return;
                }
                
                if(evt.getKeyCode() == evt.VK_UP){
                    return;
                }
                
                if(evt.getKeyCode() == evt.VK_DOWN){
                    return;
                }
                
                if(evt.getKeyCode() == evt.VK_PAGE_DOWN){
                    return;
                }
                if(evt.getKeyCode() == evt.VK_PAGE_UP){
                    return;
                }
                
                
                evt.consume();
                return;
        
            }
        });                 
    }
    
    // -----------------------------------------------------
    private void m_KeyTyped(java.awt.event.KeyEvent evt) {                                        
        if(evt.isControlDown()){
            // Dont want to process event if control is pressed, yo...
            return;
        }
        if(evt.isAltDown()){
            // Dont want to process event if alt is pressed, yo...
            return;
        }

        evt.consume();
        return;        
    }    

    
    // -----------------------------------------------------
    public int display(String Output){
        
        int i = Output.length();
        if(i <= 0){
            return 1;
        }
        
        // Dont enable commands until a command is requested....
        CmdLock = 1;

        addText(Output, StandardAttr);
        return 1;
    }
    
    // -----------------------------------------------------
    public int displayFormatted(String Output){
        int i = Output.length();
        if(i <= 0){
            return 1;
        }
        addText(Output, StandardAttr);
        return 1;
    }
    
    // -----------------------------------------------------
    public int displayError(String Output){
        addText(Output, ErrorAttr);
        if(Output != "\n"){
            int sz = Displays.size();
            for(int i = 0; i < sz; i++){
                Displays.get(i).addError();
            }
        }
        return 1;
    }
    
    // -----------------------------------------------------
    public int displayWarning(String Output){
        addText(Output, WarningAttr);
        if(Output != "\n"){
            int sz = Displays.size();
            for(int i = 0; i < sz; i++){
                Displays.get(i).addWarn();
            }
        }
        return 1;
    }

    // -----------------------------------------------------
    public int displayFailure(String Output){
        addText(Output, FailureAttr);
        return 1;
    }
    
    // -----------------------------------------------------
    public int startSuite(String Name){
        iLevel = iLevel + 1;
        return createDisplay(Name, SuiteBase.SUITE_TYPE);
    }
    
    // -----------------------------------------------------
    public int stopSuite(){
        iLevel = iLevel - 1;
        CurrDisplay.setRunning(false);
        if(Displays.size() > 1){
            Displays.remove(Displays.lastElement());
            CurrDisplay = (PTDSuiteDisplay)Displays.lastElement();
            OutputDisplay = CurrDisplay;
        }else{
            OutputDisplay = null;
        }
        return 1;
    }
    
    // -----------------------------------------------------
    public int getLevel(){
        return iLevel;
    }
    
    // -----------------------------------------------------
    public int startTestcase(String Name){
        iLevel = iLevel + 1;
        return createDisplay(Name, SuiteBase.TESTCASE_TYPE);
    }

    // -----------------------------------------------------
    public int startProcessor(int iType, String Name){
        iLevel = iLevel + 1;
        return createDisplay(Name, iType);
    }

    // -----------------------------------------------------
    public int stopProcessor(){
        iLevel = iLevel - 1;
        if(Displays.size() > 1){
            Displays.remove(Displays.lastElement());
            CurrDisplay = (PTDSuiteDisplay)Displays.lastElement();
        }
        return 1;
    }
    
    // -----------------------------------------------------
    public int stopTestcase(){
        iLevel = iLevel - 1;
        CurrDisplay.setRunning(false);
        if(Displays.size() > 1){
            Displays.remove(Displays.lastElement());
            CurrDisplay = (PTDSuiteDisplay)Displays.lastElement();
            OutputDisplay = CurrDisplay;
        }else{
            OutputDisplay = null;
        }
        return 1;
    }

    // -----------------------------------------------------
    public int startScript(String Name){
        return createDisplay(Name, SuiteBase.SCRIPT_TYPE);
    }    

    // -----------------------------------------------------
    public int stopScript(){
        return stopTestcase();
    }    
    
    // -----------------------------------------------------
    public int createDisplay(String Name, int Type){
        
        //this.setLayout( new GridBagLayout());
        //javax.swing.JButton aButton = new javax.swing.JButton("Spleen");
        //javax.swing.JLabel aLabel = new javax.swing.JLabel("Spleen");
        //aLabel.setForeground(Color.RED);
        //aLabel.setVerticalAlignment(aLabel.TOP);     // Why does this crap not work?
        //aLabel.setVerticalTextPosition(aLabel.TOP);  // Why does this crap not work?
        //aLabel.setBounds(0, 0, 1000, 1000);          // Why does this crap not work?
        //aLabel.setSize(1000, 1000);                  // Why does this crap not work?        
        //aButton.setBounds(0, 0, 100, 100);
        //aButton.setSize(400, 400);
        //aButton.setLocation(10, 10);
        //aButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        //StyleConstants.setAlignment(TestcaseAttr, StyleConstants.ALIGN_CENTER);
        //StyleConstants.setFirstLineIndent(TestcaseAttr, 12.0f);
        //StyleConstants.setComponent(TestcaseAttr, aButton);
        //StyleConstants.setComponent(TestcaseAttr, aLabel);
        //StyleConstants.setComponent(label, aLabel);
        //CurrDisplay.Label.setFont(new Font(StyleConstants.getFontFamily(TestcaseAttr), Font.BOLD, StyleConstants.getFontSize(TestcaseAttr)));
        //CurrDisplay.Label.setBackground(Color.BLUE); // Why does this crap not work? Oh, it works for border
        //CurrDisplay.Label.setBorder(new javax.swing.border.EtchedBorder());

        SimpleAttributeSet label = new SimpleAttributeSet();

        PTDSuiteDisplay ParDisplay = CurrDisplay;
        CurrDisplay = new PTDSuiteDisplay();
        CurrDisplay.my_Name = Name;
        Displays.add(CurrDisplay);
        
        Date aDate = new java.util.Date();
        String DateString = "";  // Having this just clutters things up...

        if(Type == SuiteBase.SUITE_TYPE){
            CurrDisplay.setSuite(true);
            CurrDisplay.Label.setText("Suite: " + Name + DateString);
            OutputDisplay = CurrDisplay;
            CurrDisplay.setRunning(true);
            if(ParDisplay != null){
                ParDisplay.addDisplay(CurrDisplay);
            }
        }else if(Type == SuiteBase.TESTCASE_TYPE){
            CurrDisplay.Label.setText("Testcase: " + Name + DateString);
            OutputDisplay = CurrDisplay;
            CurrDisplay.setRunning(true);
            if(ParDisplay != null){
                ParDisplay.addDisplay(CurrDisplay);
            }
        }else if(Type == SuiteBase.PREPROC_TYPE){
            CurrDisplay.Label.setText("Preprocess: " + Name + DateString);
            Font F = CurrDisplay.Label.getFont();
            CurrDisplay.Label.setFont(new Font(F.getName(), F.getStyle(), F.getSize() - 4));
            CurrDisplay.Label.setBackground(Color.LIGHT_GRAY);
            CurrDisplay.setBackground(Color.LIGHT_GRAY);
 
            // MUST BE DONE, ELSE PRINT WONT CATCH THE BORDERS
            if(ParDisplay != null){
                ParDisplay.preProc = CurrDisplay;
            }

            
            // Do not set OutputDisplay, as this is nested in the parent object
        }else if(Type == SuiteBase.POSTPROC_TYPE){
            CurrDisplay.Label.setText("Postprocess: " + Name + DateString);
            Font F = CurrDisplay.Label.getFont();
            CurrDisplay.Label.setFont(new Font(F.getName(), F.getStyle(), F.getSize() - 4));
            CurrDisplay.Label.setBackground(Color.LIGHT_GRAY);
            CurrDisplay.setBackground(Color.LIGHT_GRAY);

            // MUST BE DONE, ELSE PRINT WONT CATCH THE BORDERS
            if(ParDisplay != null){
                ParDisplay.postProc = CurrDisplay;
            }

            // Do not set OutputDisplay, as this is nested in the parent object
        }else{ // This is for Script Type
            
            CurrDisplay.Label.setText(Name + DateString);
            CurrDisplay.setRunning(true);
            OutputDisplay = CurrDisplay;
            CurrDisplay.setExpanded(true);
        }
        
        //if((iLevel % 2) > 0){
        //    CurrDisplay.Label.setBackground(Color.CYAN);
        //}else{
        //    CurrDisplay.Label.setBackground(Color.LIGHT_GRAY);
        //}
        
        StyleConstants.setComponent(label, CurrDisplay);
        
        try{
            StyledDocument aDoc;
            //Document aDoc;
            if(ParDisplay != null){
                //aDoc = ParDisplay.TestcaseOutput.getStyledDocument();
                
                aDoc = ParDisplay.TestcaseOutput.getStyledDocument();
                //aDoc = ParDisplay.SuiteOutput.getStyledDocument();
                
            }else{
                aDoc = getStyledDocument();
            }
            
            aDoc.insertString(aDoc.getLength(), "\n", label);
            //aDoc.insertString(aDoc.getLength(), "\n", TestcaseAttr);
            //aDoc.in

            aDoc.setCharacterAttributes(aDoc.getLength()-1, 1, TestcaseAttr, false);
        }catch (Exception e){
            Main.debug_out(e.getMessage());
        }
               
        return 1;
    }
        
    // -----------------------------------------------------
    public String PrepareOutput(String Output){
        return Output;
    }

    // -----------------------------------------------------
    public void addRawText(String newText){
        addText(newText, StandardAttr);
    }    
    
    // -----------------------------------------------------
    public void addText(String newText, SimpleAttributeSet Attr){
        if(CurrDisplay != null){
            CurrDisplay.addText(newText, Attr);
        }
    }

    // -----------------------------------------------------
    public boolean lineAvailable(){
        // Maybe make this an init thing...
        
        int sz = Displays.size();
        
        if(CurrDisplay.lineAvailableCount == 0){
            for(int i = 0; i < sz; i++){
                Displays.get(i).setExpanded(true);           
            } 
        }
        if(CurrDisplay.lineAvailableCount == 1){

            this.setVisible(false);  // Evidently, this setVisible crap does the trick
            Rectangle R = CurrDisplay.TestcaseOutput.getBounds();
            R = javax.swing.SwingUtilities.convertRectangle(CurrDisplay.TestcaseOutput, R, this);
            R.y = R.y + R.height - 2;
            R.height = 2;
            scrollRectToVisible(R);
            this.setVisible(true);
            CurrDisplay.TestcaseOutput.requestFocus();
        }
        return CurrDisplay.lineAvailable();
    }
    
    // -----------------------------------------------------
    public void setRunState(int newState){
    }
    
    // -----------------------------------------------------
    public String getNextLine(){
        return getInput();
    }
    
    // -----------------------------------------------------
    public String getInput(){
        return CurrDisplay.getInput();        
    }

    // -----------------------------------------------------
    public int requestDebug(String LineInfo){
        return 0;
    }
 
    // -----------------------------------------------------
    public String getDebugCmd(){
        return "";
    }
    
    // -----------------------------------------------------
    public int displayDebug(String Output){
        return 1;
    }
    
    // -----------------------------------------------------
    public void reportFault(int iCode, String Message){
        display(Message);
    }

    // -----------------------------------------------------
    public void paint(java.awt.Graphics g) {
        super.paint(g);
    }

    // -----------------------------------------------------
    public void cut() {
        // Do not want an output pane to be able to be cut...
        //super.cut();
    }
    
    // -----------------------------------------------------
    public int print (Graphics g, PageFormat pf, int pageIndex) throws PrinterException{

        // OK, so things learned:
        // 1.  getClipBounds returns different bounds after each translate
        // 2.  *Adjusting* the clip then moves the draw area within the control
        //     specified in paint.  The origin of the control appears to be at
        //     the non-adjusted clip origin. So... the control is always painted
        //     at the location where the g2 is translated to.  However, only the
        //     portion of the control set by the clip *adjustments* is painted.
        // 3.  If want to move control later in page, translate to beginning of
        //     page using getImageable.  Then get the clip, then translate to
        //     location in page, then paint.
        
        // So, essentially, translate moves the pen to where the drawing of
        // a particular object will start.  The clip is where the pen moves to
        // inside the object and the region that is drawn.
        
        
        int method = 2;
        
        if(method == 1){
            Graphics2D g2 = (Graphics2D)g;                          // Re-cast the graphics object

            double pageHeight = pf.getImageableHeight();
            Rectangle r = new Rectangle();
            PTDSuiteDisplay aDisplay = Displays.get(0);


            r = g2.getClipBounds(r);
            if(pageIndex == 0){
                r.height = 50;
                r.width = 50;
            }else if(pageIndex == 1){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r.height = 50;
                r.width = 50;
            }else if(pageIndex == 2){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r = g2.getClipBounds(r);
            }else if(pageIndex == 3){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r = g2.getClipBounds(r);
                r.x = r.x + 100;
                r.y = r.y + 10;
                r.height = 50;
                r.width = 50; 
            }else if(pageIndex == 4){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r = g2.getClipBounds(r);
                g2.translate(0.0f, 400.0f);
                r.x = r.x + 100;
                r.y = r.y + 10;
                r.height = 50;
                r.width = 50;             
            }else if(pageIndex == 5){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r = g2.getClipBounds(r);
                g2.setClip(r);
                aDisplay.paint(g2);    
                g2.translate(0, aDisplay.getHeight());
                aDisplay.paint(g2);
                g2.translate(0, aDisplay.getHeight());
                aDisplay.paint(g2);
            }else if(pageIndex == 6){
                g2.translate(pf.getImageableX(), pf.getImageableY());
                r = g2.getClipBounds(r);
                g2.translate(0, -pageHeight); // Translates the control origin up a page
                r.y = r.y + (int)pageHeight;  // Go to next page in control
                g2.setClip(r);
                aDisplay.paint(g2);    
            }else{
                return Printable.NO_SUCH_PAGE;
            }

            g2.setClip(r);
            aDisplay.paint(g2);

            RepaintManager.currentManager(this).setDoubleBufferingEnabled(false);   // Un-double buffer
            return Printable.PAGE_EXISTS;
        }else{
            
            Graphics2D g2 = (Graphics2D)g;                          // Re-cast the graphics object
            Rectangle r = new Rectangle();
           
            double topMargin = 0;  // 1/4 inch
            double botMargin = 0;  // 1/4 inch
            double leftMargin = 0;  // 1/4 inch
            double rightMargin = 0;  // 1/4 inch

            double pageWidth = pf.getImageableWidth() - (leftMargin+rightMargin);
            double pageHeight = pf.getImageableHeight() - (topMargin+botMargin);            
            
            PTDSuiteDisplay aDisplay = Displays.get(0);
            if(aDisplay == null){
                return Printable.NO_SUCH_PAGE;
            }

            if(Bounder.isInitialized == false){
                Bounder.calcPrintBounds(pf, aDisplay);
            }
            if (pageIndex >= Bounder.numPages) return Printable.NO_SUCH_PAGE;
            
            RepaintManager.currentManager(this).setDoubleBufferingEnabled(false);   // Un-double buffer
            
            Point CP = Bounder.Bounds.get(pageIndex);
            
            Main.debug_out("Printing: " + Integer.toString(CP.x) + "," + Integer.toString(CP.y));
            
            // Move pen to upper-left of first page
            g2.translate(pf.getImageableX(), pf.getImageableY());
            
            // Get the current page clip
            r = g2.getClipBounds(r);
            
            //g2.setColor(Color.GREEN);
            //g2.fill(r);
            
            // Backoff so clip start will be at top of page
            g2.translate(0, -(CP.x));  

            // Adjust the clip so it shows only the region of interest
            r.y = CP.x+1;
            r.height = (CP.y-CP.x);
            
            // Set the clip
            g2.setClip(r);
            
            // Paint the control at the translation point, but only of the clip
            // specified
            aDisplay.paint(g2);

            // Set the clip and draw page number
            if(CurrDisplay != null){
                g2.setClip(r.x, r.y, r.width, r.height + 54);
                Font F = CurrDisplay.TestcaseOutput.getFont();
                g2.setFont(F);
                
                double titleX = (pf.getImageableWidth () / 2) - (CurrDisplay.TestcaseOutput.getFontMetrics(F).stringWidth (String.valueOf(pageIndex+1)) / 2);
                g2.drawString (String.valueOf(pageIndex+1), (int)titleX, (int)(28 + CP.x + pageHeight));
            }
            
            
            RepaintManager.currentManager(this).setDoubleBufferingEnabled(true);   // Un-double buffer
            
            return Printable.PAGE_EXISTS;
        }        
        //if(pageIndex == lastPrintedPage){
        //    //printBackoff = printBackoff + backoff;
        //    return Printable.PAGE_EXISTS;
        //}else{
        //    //g2.translate(0, -backoff);
        //}
        
   
        
   
        //Dimension d = this.getSize();
        //double panelWidth = d.width;
        //double panelHeight = d.height;
/*
        
        double topMargin = 0;  // 1/4 inch
        double botMargin = 0;  // 1/4 inch
        double leftMargin = 0;  // 1/4 inch
        double rightMargin = 0;  // 1/4 inch
        
        double pageWidth = pf.getImageableWidth() - (leftMargin+rightMargin);
        double pageHeight = pf.getImageableHeight() - (topMargin+botMargin);

        
        double scale = 1.0;      // Descale the text so more fits on a page
        //double line_margin = 54;  // 3/4 an inch for line numbers      
        //int iH = getLineHeight();            // Calculate line height
        //int nH = (int)(iH*scale);            // Descale line height
        //scale = ((double)nH)/((double)iH);   // Adjust scale for integer values of pixel sizes
        //iH = nH;                             // Save scaled height
        //int NumLines = (int)(pageHeight/iH); // Get number of scaled lines

//        g2.setStroke (new BasicStroke (1));
//        Rectangle2D.Double border = new Rectangle2D.Double(5, 5,  pageWidth,  pageHeight);
//        g2.draw(border);
        
        
        PTDSuiteDisplay aDisplay = Displays.get(0);
        if(aDisplay == null){
            return Printable.NO_SUCH_PAGE;
        }
        
        if(Bounder.isInitialized == false){
            Bounder.calcPrintBounds(pf, aDisplay);
        }
        
        //int dispH = aDisplay.getHeight();
        //int totalNumPages = (int)Math.ceil(scale * dispH / pageHeight);
        
        // Check for empty pages
        //if (pageIndex >= totalNumPages) return Printable.NO_SUCH_PAGE;
        if (pageIndex >= Bounder.numPages) return Printable.NO_SUCH_PAGE;
        
        //if (pageIndex >= 1) return Printable.NO_SUCH_PAGE;
        
        //double yStart = -pageIndex * pageHeight + topMargin + printBackoff; 
        //double yEnd = yStart + pageHeight;

        // g2 represents the page canvas, *not* the control
        // so translate to the upper left corner of the page
        g2.translate(pf.getImageableX(), pf.getImageableY());

        g2.setStroke (new BasicStroke (2));
        Rectangle2D.Double border = new Rectangle2D.Double(leftMargin-28, topMargin-28,  pageWidth+28,  pageHeight+28);
        //Rectangle2D.Double border = new Rectangle2D.Double(pf.getImageableX(), pf.getImageableY(),  pageWidth,  pageHeight);
        g2.draw(border);
        
        Point CP = Bounder.Bounds.get(pageIndex);
        // Translate to the beginning of the page of interest
        
        //g2.translate(leftMargin, -CP.x);
        
        
        //int backoff = 0;            
        //int a = aDisplay.getWidth()/2;
        //int b = (int)yEnd;
        //if(aDisplay.contains(a, b)){
        //    backoff = aDisplay.computePrintBackoff((int)(yEnd));
        //}            
                  
        
        // Determine which component is 'end of page'
        // Determine if header, footer, or text is 'end of page'
        // If header, footer, automatically extend to next page
        // If text, determine if falls on line
        //   If so, back up one line
        
        // Set the font to the font of the page
        //g2.setFont(this.getFont());
        //Font F = this.getFont();

        // Re-set the clip so only prints an integer number of lines
        
        g2.translate(leftMargin, -(int)pageHeight*pageIndex);

        
        // SETTING THE CLIP SETS THE CLIP OF THE COMPONENT?
        // TRANSLATE TO WHERE YOU WANT TO BE
        // SET CLIP OF COMPONENT
        // PAINT COMPONENT TO G2
        // MUST BACK-TRANSLATE TO COMPONENT CLIP OFFSET?
        Rectangle r = new Rectangle();
        r = g2.getClipBounds(r);
        //r.x = r.x;// + (int)leftMargin;
        
        
        Main.debug_out("Printing: " + Integer.toString(CP.x) + "," + Integer.toString(CP.y));
        
        
        //r.y = CP.x+100;//(int)pageHeight*pageIndex;
        //r.height = CP.y - CP.x + 1;
        //r.height = 45;
        //r.y = CP.x; //r.y;// + (int)topMargin;
        r.height = CP.y - CP.x + 1; // +1 because inclusive
        r.width = (int)pageWidth;
        
       
        
        //r.y = r.y + this.getFontMetrics(F).getDescent();
        //r.x = r.x - (int)line_margin;
        //r.width = r.width + (int)line_margin;
        //r.height = NumLines*iH;  // Oh just a fudge, dangit...
        
        // Set the clip and draw title
        //g2.setClip(r.x, r.y-54, r.width, r.height);
        //double titleX = (pf.getImageableWidth () / 2) - (this.getFontMetrics(F).stringWidth (PrintName) / 2);
        //titleX = titleX - line_margin;
        //g2.drawString (PrintName, (int)titleX, -28 + pageIndex*iH*NumLines);
        //g2.setClip(r.x, r.y, r.width, r.height);
        
        // Set the clip and draw page number
        //g2.setClip(r.x, r.y, r.width, r.height + 54);
        //titleX = (pf.getImageableWidth () / 2) - (this.getFontMetrics(F).stringWidth (String.valueOf(pageIndex+1)) / 2);
        //titleX = titleX - line_margin;
        //g2.drawString (String.valueOf(pageIndex+1), (int)titleX, 28 + (pageIndex+1)*iH*NumLines);
        g2.setClip(r.x, r.y, r.width, r.height);

        // Hide the caret and paint the page
        Caret c = this.getCaret();
        c.setVisible(false);
        //g2.scale(scale, scale);
        this.paint(g2);
        
        // Paint line numbers
        //int ctr = 0;
        //int TotalLines = lineFromPos(this.getText().length());
        //int lmH = this.getFontMetrics(F).getHeight();
        //int iX = (int)(-1*line_margin);
        //int iY = pageIndex*lmH*NumLines; //(int)(pf.getImageableY() + pageIndex*iH*NumLines);
        //for(ctr = 0; ctr < NumLines; ctr++){
        //    iY = iY + lmH;
        //    if(ctr+pageIndex*NumLines+1 <= TotalLines){
        //        g2.drawString (String.valueOf(ctr+pageIndex*NumLines+1) + ":", iX, iY);
        //    }
        //}
        
        //g2.scale(1/scale, 1/scale);
        c.setVisible(true);

        

        //lastPrintedPage = pageIndex;
        return Printable.PAGE_EXISTS;
        
        
        //CurrDisplay.printAll(g);
        //CurrDisplay.print(g);
        //if(pageIndex > 0){
        //    return Printable.NO_SUCH_PAGE;
        //}
        
        //Graphics2D g2d = (Graphics2D)g;
        //g2d.translate(pf.getImageableX(), pf.getImageableY());
        
        //CurrDisplay.print(g);
        //return Printable.PAGE_EXISTS;
 **/ 
    }

    /*
    public int getNumberOfPages() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public PageFormat getPageFormat(int pageIndex) throws IndexOutOfBoundsException {
        PageFormate pf
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public Printable getPrintable(int pageIndex) throws IndexOutOfBoundsException {
        throw new UnsupportedOperationException("Not supported yet.");
    }
    
    public void calcPages(PageFormat pf){
        double topMargin = 18;  // 1/4 inch
        double botMargin = 18;  // 1/4 inch
        
        double pageHeight = pf.getImageableHeight() - (topMargin+botMargin);

        double scale = 1.0;      // Descale the text so more fits on a page

        PTDSuiteDisplay aDisplay = Displays.get(0);
        numPages = 0;
        if(aDisplay == null){
            
            return;
        }
        int dispH = aDisplay.getHeight();
        int totalNumPages = (int)Math.ceil(scale * dispH / pageHeight);
    }
    **/
    
    public void collapseAll(){
        PTDSuiteDisplay aDisplay = Displays.get(0);
        if(aDisplay == null){
            return;
        }
        aDisplay.collapseAll();
        aDisplay.validate();
        //aDisplay.u
        //validate();
    }
    
    public void expandAll(){
        PTDSuiteDisplay aDisplay = Displays.get(0);
        if(aDisplay == null){
            return;
        }     
        aDisplay.expandAll();
    }
    
    public void export(String sPath, int Type){
        
        PTDSuiteDisplay aDisplay = Displays.get(0);
        if(aDisplay == null){
            return;
        }

        try{
            // This is how we write
            File aFile = new File(sPath);
            FileOutputStream anOutput = new FileOutputStream(aFile);
            OutputStreamWriter aWriter = new OutputStreamWriter(anOutput);
            if(Type == 1){
                aWriter.write("<HTML><HEAD><TITLE>PTD Output</TITLE>\n");
                //aWriter.write("<style>\n.toggle{cursor:pointer;}\n.hidden{display:none;}\n.shown{display:block;\nwhite-space: nowrap;\n}\n</style>\n");
                aWriter.write("</HEAD>\n");
                //aWriter.write("<script language='JavaScript'>\n<!--\nfunction toggle(section_name){\nif(section_name.className.match('hidden')){\nsection_name.className = 'shown';\n}else{\nsection_name.className = 'hidden';\n}\n}\n//-->\n</SCRIPT>\n");
                aWriter.write("<BODY>\n");
                aWriter.write("<font face = \"Courier New\">\n");
            }

            aDisplay.export(aWriter, Type, "", 0, false);
            if(Type == 1){
                aWriter.write("</font>");
                aWriter.write("</BODY></HTML>\n");
            }

            aWriter.flush();
            aWriter.close();
            
        }catch (Exception e){
            Main.debug_out(e.getMessage());
            return;
        }
    }
    
    public void setRemoteMachine(PTDRemoteMachine In){
        
        if(OutputDisplay != null){
            OutputDisplay.Machine = In;
        }
        if(CurrDisplay != null){
            CurrDisplay.Machine = In;
        }
    }
    
}
